#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polygon_2.h>
#include <CGAL/Polygon_with_holes_2.h>
#include <CGAL/Polygon_set_2.h>

#include <list>
#include <stdlib.h>

#include "cgpoly.h"

#define NUM_PA 5

typedef CGAL::Simple_cartesian<double> Kernel;
// typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef CGAL::Polygon_2<Kernel>                           Polygon_2;
typedef CGAL::Polygon_with_holes_2<Kernel>                Polygon_with_holes_2;
typedef CGAL::Polygon_set_2<Kernel>                       Polygon_set_2;

Polygon_set_2 gpa[NUM_PA];
std::vector<Polygon_with_holes_2> gpa_phl[NUM_PA];
Polygon_with_holes_2::Hole_const_iterator ch;

std::vector<Polygon_2> plist;
Polygon_2 cp;
Polygon_2 p_empty;

int id_pa;
int is_bound;
int id_poly;

void PASetNewPA (int id)
{
      gpa[id].clear();
      id_pa = id;
}

void PASetNewPoly (char is_bound_)
{
      plist.clear();
      is_bound = is_bound_;
}

void PASetNewBound ()
{
      cp.clear();
}

void PASetNewPoint (double x, double y)
{
      cp.push_back (Point_2(x, y));
}

void PASetEndBound ()
{
      plist.push_back (cp);
}

void PASetEndPoly ()
{
      Polygon_with_holes_2 * ph; 
      if (is_bound)
            ph = new Polygon_with_holes_2 (plist[0], plist.begin() + 1, plist.end());
      else 
            ph = new Polygon_with_holes_2 (p_empty, plist.begin(), plist.end());

      gpa[id_pa].insert (*ph);
      delete ph;
} 

void PASetEndPA ()
{
      gpa_phl[id_pa].clear();
      gpa[id_pa].polygons_with_holes (std::back_inserter (gpa_phl[id_pa]));
}

int PAGetNPoly (int id)
{
      return gpa[id].number_of_polygons_with_holes();
}

char PAGetIsBound (int id, int ipoly)
{
      if (gpa_phl[id][ipoly].is_unbounded()) 
            return 0;
      else
            return 1;
}

int PAGetOutterBoundNPoint (int id, int ipoly)
{
      return gpa_phl[id][ipoly].outer_boundary().size();
}

double PAGetOBX (int id, int ipoly, int iv)
{
      iv %= PAGetOutterBoundNPoint (id, ipoly);
      return gpa_phl[id][ipoly].outer_boundary()[iv].x();
}
double PAGetOBY (int id, int ipoly, int iv)
{
      iv %= PAGetOutterBoundNPoint (id, ipoly);
      return gpa_phl[id][ipoly].outer_boundary()[iv].y();
}
int PAGetNHole (int id, int ipoly)
{
      return gpa_phl[id][ipoly].number_of_holes();
}

void PAIterateHoleBegin (int id, int ipoly) 
{
      ch = gpa_phl[id][ipoly].holes_begin();
      id_pa = id;
      id_poly = ipoly;
}

void PAIterateHoleNext ()
{
      ++ ch;
}

int PAIterateHoleIsContinue ()
{
      return (ch != gpa_phl[id_pa][id_poly].holes_end());
}

int PAGetHoleNPoint ()
{
      return ch->size();
}

double PAGetHoleX (int iv)
{
      iv %= PAGetHoleNPoint ();
      return (*ch)[iv].x();
}

double PAGetHoleY (int iv)
{
      iv %= PAGetHoleNPoint ();
      return (*ch)[iv].y();
}

void PADiff (int pa1, int pa2, int res)
{
      gpa[res].difference (gpa[pa1], gpa[pa2]);
      gpa_phl[res].clear();
      gpa[res].polygons_with_holes (std::back_inserter (gpa_phl[res]));
}

void PAUnion (int pa1, int pa2, int res)
{
      gpa[res].join (gpa[pa1], gpa[pa2]);
      gpa_phl[res].clear();
      gpa[res].polygons_with_holes (std::back_inserter (gpa_phl[res]));
}

void PAInter (int pa1, int pa2, int res)
{
      gpa[res].intersection (gpa[pa1], gpa[pa2]);
      gpa_phl[res].clear();
      gpa[res].polygons_with_holes (std::back_inserter (gpa_phl[res]));
}

void PAPrint (char * fout_name, int id)
{
      int i, j, k;

      FILE * fout = fopen (fout_name, "w");
      fprintf (fout, "size(10cm, true);\n\n");

      int npoly = PAGetNPoly (id);

      for (i = 0; i < npoly; ++ i) 
      {
            int np_ob = PAGetOutterBoundNPoint (id, i);
            for (j = 0; j < np_ob; ++ j)
            {
                  fprintf (fout, "draw ((%12.4lf, %12.4lf) -- (%12.4lf, %12.4lf));\n", 
                        PAGetOBX (id, i, j), PAGetOBY (id, i, j), PAGetOBX (id, i, j + 1), PAGetOBY (id, i, j + 1));
            }
            for (PAIterateHoleBegin (id, i); PAIterateHoleIsContinue() ; PAIterateHoleNext()) 
            {
                  np_ob = PAGetHoleNPoint(); 
                  for (j = 0; j < np_ob; ++ j) 
                  {
                        fprintf (fout, "draw ((%12.4lf, %12.4lf) -- (%12.4lf, %12.4lf), blue);\n", 
                              PAGetHoleX(j), PAGetHoleY(j), PAGetHoleX(j + 1), PAGetHoleY(j + 1));
                  }
            }
      }

      fclose (fout);
}

